#ifndef _DEAPPENDLIST_HPP
#define _DEAPPENDLIST_HPP
/*-------------------------------------------------------------------------
 * drawElements C++ Base Library
 * -----------------------------
 *
 * Copyright 2015 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 *//*!
 * \file
 * \brief Fast ordered append-only container
 *//*--------------------------------------------------------------------*/

#include "deDefs.hpp"
#include "deAtomic.h"
#include "deThread.h"
#include "deMemory.h"
#include "deInt32.h"

namespace de
{

/*--------------------------------------------------------------------*//*!
 * \brief Fast ordered append-only container
 *
 * AppendList provides data structure for recording ordered list of elements
 * quickly, while still providing good sequential read access speed.
 * It is good for example logging.
 *
 * AppendList allocates memory in blocks of blockSize elements. Choosing
 * too small blockSize will affect performance.
 *
 * Elements can be appended from multiple threads simultaneously but if
 * current block runs out, allocation of next block will happen in a single
 * thread and block others from inserting further elements until completed.
 * For that reason shared AppendList should not be used if there is a lot
 * of contention and instead per-thread AppendList's are recommended.
 *//*--------------------------------------------------------------------*/
template<typename ElementType>
class AppendList
{
public:
								AppendList		(size_t blockSize);
								~AppendList		(void);

	void						append			(const ElementType& value);

	size_t						size			(void) const { return m_numElements;	}

	void						clear			(void);

private:
								AppendList		(const AppendList<ElementType>&);
	AppendList<ElementType>&	operator=		(const AppendList<ElementType>&);

	struct Block
	{
		const size_t		blockNdx;
		ElementType*		elements;
		Block* volatile		next;

		Block (size_t blockNdx_, size_t size)
			: blockNdx	(blockNdx_)
			, elements	(reinterpret_cast<ElementType*>(deAlignedMalloc(sizeof(ElementType)*size,
																		deAlign32((deUint32)alignOf<ElementType>(), (deUint32)sizeof(void*)))))
			, next		(DE_NULL)
		{
		}

		~Block (void)
		{
			deAlignedFree(reinterpret_cast<void*>(elements));
		}
	};

	const size_t				m_blockSize;
	volatile size_t				m_numElements;
	Block*						m_first;
	Block* volatile				m_last;

public:
	template<typename CompatibleType>
	class Iterator
	{
	public:
									Iterator						(Block* curBlock_, size_t blockSize_, size_t slotNdx_)
																		: m_curBlock	(curBlock_)
																		, m_blockSize	(blockSize_)
																		, m_slotNdx		(slotNdx_)
		{}

		bool						operator!=						(const Iterator<CompatibleType>& other) const
		{
			return m_curBlock != other.m_curBlock || m_slotNdx != other.m_slotNdx;
		}
		bool						operator==						(const Iterator<CompatibleType>& other) const
		{
			return m_curBlock == other.m_curBlock && m_slotNdx == other.m_slotNdx;
		}

		Iterator<CompatibleType>&	operator++						(void)
		{
			++m_slotNdx;

			if (m_slotNdx == m_blockSize)
			{
				m_slotNdx = 0;
				m_curBlock = m_curBlock->next;
			}

			return *this;
		}

		Iterator<CompatibleType>	operator++						(int) const
		{
			Iterator<CompatibleType> copy(*this);
			return ++copy;
		}

		CompatibleType&				operator*						(void) const
		{
			return m_curBlock->elements[m_slotNdx];
		}

		CompatibleType*				operator->						(void) const
		{
			return &m_curBlock->elements[m_slotNdx];
		}

		operator					Iterator<const CompatibleType>	(void) const
		{
			return Iterator<const CompatibleType>(m_curBlock, m_blockSize, m_slotNdx);
		}

	private:
		Block*			m_curBlock;
		size_t			m_blockSize;
		size_t			m_slotNdx;
	};

	typedef Iterator<const ElementType>	const_iterator;
	typedef Iterator<ElementType>		iterator;

	const_iterator				begin			(void) const;
	iterator					begin			(void);

	const_iterator				end				(void) const;
	iterator					end				(void);
};

template<typename ElementType>
AppendList<ElementType>::AppendList (size_t blockSize)
	: m_blockSize	(blockSize)
	, m_numElements	(0)
	, m_first		(new Block(0, blockSize))
	, m_last		(m_first)
{
}

template<typename ElementType>
AppendList<ElementType>::~AppendList (void)
{
	size_t	elementNdx	= 0;
	Block*	curBlock	= m_first;

	while (curBlock)
	{
		Block* const	delBlock	= curBlock;

		curBlock = delBlock->next;

		// Call destructor for allocated elements
		for (; elementNdx < min(m_numElements, (delBlock->blockNdx+1)*m_blockSize); ++elementNdx)
			delBlock->elements[elementNdx%m_blockSize].~ElementType();

		delete delBlock;
	}

	DE_ASSERT(elementNdx == m_numElements);
}

template<typename ElementType>
void AppendList<ElementType>::clear (void)
{
	// \todo [2016-03-28 pyry] Make thread-safe, if possible

	size_t	elementNdx	= 0;
	Block*	curBlock	= m_first;

	while (curBlock)
	{
		Block* const	delBlock	= curBlock;

		curBlock = delBlock->next;

		// Call destructor for allocated elements
		for (; elementNdx < min(m_numElements, (delBlock->blockNdx+1)*m_blockSize); ++elementNdx)
			delBlock->elements[elementNdx%m_blockSize].~ElementType();

		if (delBlock != m_first)
			delete delBlock;
	}

	DE_ASSERT(elementNdx == m_numElements);

	m_numElements	= 0;
	m_first->next	= DE_NULL;
	m_last			= m_first;
}

template<typename ElementType>
void AppendList<ElementType>::append (const ElementType& value)
{
	// Fetch curBlock first before allocating slot. Otherwise m_last might get updated before
	// this thread gets chance of reading it, leading to curBlock->blockNdx > blockNdx.
	Block*			curBlock	= m_last;

	deMemoryReadWriteFence();

	{
		const size_t	elementNdx	= deAtomicIncrementUSize(&m_numElements) - 1;
		const size_t	blockNdx	= elementNdx / m_blockSize;
		const size_t	slotNdx		= elementNdx - (blockNdx * m_blockSize);

		while (curBlock->blockNdx != blockNdx)
		{
			if (curBlock->next)
				curBlock = curBlock->next;
			else
			{
				// Other thread(s) are currently allocating additional block(s)
				deYield();
			}
		}

		// Did we allocate last slot? If so, add a new block
		if (slotNdx+1 == m_blockSize)
		{
			Block* const	newBlock	= new Block(blockNdx+1, m_blockSize);

			deMemoryReadWriteFence();

			// At this point if any other thread is trying to allocate more blocks
			// they are being blocked by curBlock->next being null. This guarantees
			// that this thread has exclusive modify access to m_last.
			m_last = newBlock;
			deMemoryReadWriteFence();

			// At this point other threads might have skipped to newBlock, but we
			// still have exclusive modify access to curBlock->next.
			curBlock->next = newBlock;
			deMemoryReadWriteFence();
		}

		new (&curBlock->elements[slotNdx]) ElementType(value);
	}
}

template<typename ElementType>
typename AppendList<ElementType>::const_iterator AppendList<ElementType>::begin (void) const
{
	return const_iterator(m_first, m_blockSize, 0);
}

template<typename ElementType>
typename AppendList<ElementType>::iterator AppendList<ElementType>::begin (void)
{
	return iterator(m_first, m_blockSize, 0);
}

template<typename ElementType>
typename AppendList<ElementType>::const_iterator AppendList<ElementType>::end (void) const
{
	return const_iterator(m_last, m_blockSize, m_numElements%m_blockSize);
}

template<typename ElementType>
typename AppendList<ElementType>::iterator AppendList<ElementType>::end (void)
{
	return iterator(m_last, m_blockSize, m_numElements%m_blockSize);
}

void	AppendList_selfTest		(void);

} // de

#endif // _DEAPPENDLIST_HPP
